(function (global, factory) {
  typeof exports === "object" && typeof module !== "undefined"
    ? (module.exports = factory())
    : typeof define === "function" && define.amd
    ? define(factory)
    : (global.TWEEN = factory());
})(this, function () {
  "use strict";

  var version = "18.5.0";

  /**
   * Tween.js - Licensed under the MIT license
   * https://github.com/tweenjs/tween.js
   * ----------------------------------------------
   *
   * See https://github.com/tweenjs/tween.js/graphs/contributors for the full list of contributors.
   * Thank you all, you're awesome!
   */

  var _Group = function () {
    this._tweens = {};
    this._tweensAddedDuringUpdate = {};
  };

  _Group.prototype = {
    getAll: function () {
      return Object.keys(this._tweens).map(
        function (tweenId) {
          return this._tweens[tweenId];
        }.bind(this)
      );
    },

    removeAll: function () {
      this._tweens = {};
    },

    add: function (tween) {
      this._tweens[tween.getId()] = tween;
      this._tweensAddedDuringUpdate[tween.getId()] = tween;
    },

    remove: function (tween) {
      delete this._tweens[tween.getId()];
      delete this._tweensAddedDuringUpdate[tween.getId()];
    },

    update: function (time, preserve) {
      var tweenIds = Object.keys(this._tweens);

      if (tweenIds.length === 0) {
        return false;
      }

      time = time !== undefined ? time : TWEEN.now();

      // Tweens are updated in "batches". If you add a new tween during an
      // update, then the new tween will be updated in the next batch.
      // If you remove a tween during an update, it may or may not be updated.
      // However, if the removed tween was added during the current batch,
      // then it will not be updated.
      while (tweenIds.length > 0) {
        this._tweensAddedDuringUpdate = {};

        for (var i = 0; i < tweenIds.length; i++) {
          var tween = this._tweens[tweenIds[i]];

          if (tween && tween.update(time) === false && !preserve) {
            delete this._tweens[tweenIds[i]];
          }
        }

        tweenIds = Object.keys(this._tweensAddedDuringUpdate);
      }

      return true;
    },
  };

  var TWEEN = new _Group();

  TWEEN.Group = _Group;
  TWEEN._nextId = 0;
  TWEEN.nextId = function () {
    return TWEEN._nextId++;
  };

  // Include a performance.now polyfill.
  // In node.js, use process.hrtime.
  if (
    typeof self === "undefined" &&
    typeof process !== "undefined" &&
    process.hrtime
  ) {
    TWEEN.now = function () {
      var time = process.hrtime();

      // Convert [seconds, nanoseconds] to milliseconds.
      return time[0] * 1000 + time[1] / 1000000;
    };
  }
  // In a browser, use self.performance.now if it is available.
  else if (
    typeof self !== "undefined" &&
    self.performance !== undefined &&
    self.performance.now !== undefined
  ) {
    // This must be bound, because directly assigning this function
    // leads to an invocation exception in Chrome.
    TWEEN.now = self.performance.now.bind(self.performance);
  }
  // Use Date.now if it is available.
  else if (Date.now !== undefined) {
    TWEEN.now = Date.now;
  }
  // Otherwise, use 'new Date().getTime()'.
  else {
    TWEEN.now = function () {
      return new Date().getTime();
    };
  }

  TWEEN.Tween = function (object, group) {
    this._isPaused = false;
    this._pauseStart = null;
    this._object = object;
    this._valuesStart = {};
    this._valuesEnd = {};
    this._valuesStartRepeat = {};
    this._duration = 1000;
    this._initialRepeat = 0;
    this._repeat = 0;
    this._repeatDelayTime = undefined;
    this._yoyo = false;
    this._isPlaying = false;
    this._reversed = false;
    this._delayTime = 0;
    this._startTime = null;
    this._easingFunction = TWEEN.Easing.Linear.None;
    this._interpolationFunction = TWEEN.Interpolation.Linear;
    this._chainedTweens = [];
    this._onStartCallback = null;
    this._onStartCallbackFired = false;
    this._onUpdateCallback = null;
    this._onRepeatCallback = null;
    this._onCompleteCallback = null;
    this._onStopCallback = null;
    this._group = group || TWEEN;
    this._id = TWEEN.nextId();
    this._isChainStopped = false;
  };

  TWEEN.Tween.prototype = {
    getId: function () {
      return this._id;
    },

    isPlaying: function () {
      return this._isPlaying;
    },

    isPaused: function () {
      return this._isPaused;
    },

    to: function (properties, duration) {
      for (var prop in properties) {
        this._valuesEnd[prop] = properties[prop];
      }

      if (duration !== undefined) {
        this._duration = duration;
      }

      return this;
    },

    duration: function duration(d) {
      this._duration = d;
      return this;
    },

    start: function (time) {
      if (this._isPlaying) {
        return this;
      }

      this._group.add(this);

      this._repeat = this._initialRepeat;

      if (this._reversed) {
        // If we were reversed (f.e. using the yoyo feature) then we need to
        // flip the tween direction back to forward.

        this._reversed = false;

        var property;

        for (property in this._valuesStartRepeat) {
          this._swapEndStartRepeatValues(property);
          this._valuesStart[property] = this._valuesStartRepeat[property];
        }
      }

      this._isPlaying = true;

      this._isPaused = false;

      this._onStartCallbackFired = false;

      this._isChainStopped = false;

      this._startTime =
        time !== undefined
          ? typeof time === "string"
            ? TWEEN.now() + parseFloat(time)
            : time
          : TWEEN.now();
      this._startTime += this._delayTime;

      for (var property in this._valuesEnd) {
        // If `to()` specifies a property that doesn't exist in the source object,
        // we should not set that property in the object
        if (this._object[property] === undefined) {
          continue;
        }

        // Save the starting value only once.
        if (typeof this._valuesStart[property] !== "undefined") {
          continue;
        }

        // Check if an Array was provided as property value
        if (this._valuesEnd[property] instanceof Array) {
          var endValues = this._valuesEnd[property];

          if (endValues.length === 0) {
            continue;
          }

          var startValue = this._object[property];

          // handle an array of relative values
          endValues = endValues.map(
            this._handleRelativeValue.bind(this, startValue)
          );

          // Create a local copy of the Array with the start value at the front
          this._valuesEnd[property] = [startValue].concat(endValues);
        }

        this._valuesStart[property] = this._object[property];

        if (this._valuesStart[property] instanceof Array === false) {
          this._valuesStart[property] *= 1.0; // Ensures we're using numbers, not strings
        }

        if (this._valuesEnd[property] instanceof Array) {
          this._valuesStartRepeat[property] = this._valuesEnd[property]
            .slice()
            .reverse();
        } else {
          this._valuesStartRepeat[property] = this._valuesStart[property] || 0;
        }
      }

      return this;
    },

    stop: function () {
      if (!this._isChainStopped) {
        this._isChainStopped = true;
        this.stopChainedTweens();
      }

      if (!this._isPlaying) {
        return this;
      }

      this._group.remove(this);

      this._isPlaying = false;

      this._isPaused = false;

      if (this._onStopCallback !== null) {
        this._onStopCallback(this._object);
      }

      return this;
    },

    end: function () {
      this.update(Infinity);
      return this;
    },

    pause: function (time) {
      if (this._isPaused || !this._isPlaying) {
        return this;
      }

      this._isPaused = true;

      this._pauseStart = time === undefined ? TWEEN.now() : time;

      this._group.remove(this);

      return this;
    },

    resume: function (time) {
      if (!this._isPaused || !this._isPlaying) {
        return this;
      }

      this._isPaused = false;

      this._startTime +=
        (time === undefined ? TWEEN.now() : time) - this._pauseStart;

      this._pauseStart = 0;

      this._group.add(this);

      return this;
    },

    stopChainedTweens: function () {
      for (
        var i = 0, numChainedTweens = this._chainedTweens.length;
        i < numChainedTweens;
        i++
      ) {
        this._chainedTweens[i].stop();
      }
    },

    group: function (group) {
      this._group = group;
      return this;
    },

    delay: function (amount) {
      this._delayTime = amount;
      return this;
    },

    repeat: function (times) {
      this._initialRepeat = times;
      this._repeat = times;
      return this;
    },

    repeatDelay: function (amount) {
      this._repeatDelayTime = amount;
      return this;
    },

    yoyo: function (yoyo) {
      this._yoyo = yoyo;
      return this;
    },

    easing: function (easingFunction) {
      this._easingFunction = easingFunction;
      return this;
    },

    interpolation: function (interpolationFunction) {
      this._interpolationFunction = interpolationFunction;
      return this;
    },

    chain: function () {
      this._chainedTweens = arguments;
      return this;
    },

    onStart: function (callback) {
      this._onStartCallback = callback;
      return this;
    },

    onUpdate: function (callback) {
      this._onUpdateCallback = callback;
      return this;
    },

    onRepeat: function onRepeat(callback) {
      this._onRepeatCallback = callback;
      return this;
    },

    onComplete: function (callback) {
      this._onCompleteCallback = callback;
      return this;
    },

    onStop: function (callback) {
      this._onStopCallback = callback;
      return this;
    },

    update: function (time) {
      var property;
      var elapsed;
      var value;
      var endTime = this._startTime + this._duration;

      if (time > endTime && !this._isPlaying) {
        return false;
      }

      // If the tween was already finished,
      if (!this.isPlaying) {
        this.start(time);
      }

      if (time < this._startTime) {
        return true;
      }

      if (this._onStartCallbackFired === false) {
        if (this._onStartCallback !== null) {
          this._onStartCallback(this._object);
        }

        this._onStartCallbackFired = true;
      }

      elapsed = (time - this._startTime) / this._duration;
      elapsed = this._duration === 0 || elapsed > 1 ? 1 : elapsed;

      value = this._easingFunction(elapsed);

      for (property in this._valuesEnd) {
        // Don't update properties that do not exist in the source object
        if (this._valuesStart[property] === undefined) {
          continue;
        }

        var start = this._valuesStart[property] || 0;
        var end = this._valuesEnd[property];

        if (end instanceof Array) {
          this._object[property] = this._interpolationFunction(end, value);
        } else {
          // Parses relative end values with start as base (e.g.: +10, -3)
          end = this._handleRelativeValue(start, end);

          // Protect against non numeric properties.
          if (typeof end === "number") {
            this._object[property] = start + (end - start) * value;
          }
        }
      }

      if (this._onUpdateCallback !== null) {
        this._onUpdateCallback(this._object, elapsed);
      }

      if (elapsed === 1) {
        if (this._repeat > 0) {
          if (isFinite(this._repeat)) {
            this._repeat--;
          }

          // Reassign starting values, restart by making startTime = now
          for (property in this._valuesStartRepeat) {
            if (!this._yoyo && typeof this._valuesEnd[property] === "string") {
              this._valuesStartRepeat[property] =
                this._valuesStartRepeat[property] +
                parseFloat(this._valuesEnd[property], 10);
            }

            if (this._yoyo) {
              this._swapEndStartRepeatValues(property);
            }

            this._valuesStart[property] = this._valuesStartRepeat[property];
          }

          if (this._yoyo) {
            this._reversed = !this._reversed;
          }

          if (this._repeatDelayTime !== undefined) {
            this._startTime = time + this._repeatDelayTime;
          } else {
            this._startTime = time + this._delayTime;
          }

          if (this._onRepeatCallback !== null) {
            this._onRepeatCallback(this._object);
          }

          return true;
        } else {
          if (this._onCompleteCallback !== null) {
            this._onCompleteCallback(this._object);
          }

          for (
            var i = 0, numChainedTweens = this._chainedTweens.length;
            i < numChainedTweens;
            i++
          ) {
            // Make the chained tweens start exactly at the time they should,
            // even if the `update()` method was called way past the duration of the tween
            this._chainedTweens[i].start(this._startTime + this._duration);
          }

          this._isPlaying = false;

          return false;
        }
      }

      return true;
    },

    _handleRelativeValue: function (start, end) {
      if (typeof end !== "string") {
        return end;
      }

      if (end.charAt(0) === "+" || end.charAt(0) === "-") {
        return start + parseFloat(end);
      } else {
        return parseFloat(end);
      }
    },

    _swapEndStartRepeatValues: function (property) {
      var tmp = this._valuesStartRepeat[property];

      if (typeof this._valuesEnd[property] === "string") {
        this._valuesStartRepeat[property] =
          this._valuesStartRepeat[property] +
          parseFloat(this._valuesEnd[property], 10);
      } else {
        this._valuesStartRepeat[property] = this._valuesEnd[property];
      }

      this._valuesEnd[property] = tmp;
    },
  };

  TWEEN.Easing = {
    Linear: {
      None: function (k) {
        return k;
      },
    },

    Quadratic: {
      In: function (k) {
        return k * k;
      },

      Out: function (k) {
        return k * (2 - k);
      },

      InOut: function (k) {
        if ((k *= 2) < 1) {
          return 0.5 * k * k;
        }

        return -0.5 * (--k * (k - 2) - 1);
      },
    },

    Cubic: {
      In: function (k) {
        return k * k * k;
      },

      Out: function (k) {
        return --k * k * k + 1;
      },

      InOut: function (k) {
        if ((k *= 2) < 1) {
          return 0.5 * k * k * k;
        }

        return 0.5 * ((k -= 2) * k * k + 2);
      },
    },

    Quartic: {
      In: function (k) {
        return k * k * k * k;
      },

      Out: function (k) {
        return 1 - --k * k * k * k;
      },

      InOut: function (k) {
        if ((k *= 2) < 1) {
          return 0.5 * k * k * k * k;
        }

        return -0.5 * ((k -= 2) * k * k * k - 2);
      },
    },

    Quintic: {
      In: function (k) {
        return k * k * k * k * k;
      },

      Out: function (k) {
        return --k * k * k * k * k + 1;
      },

      InOut: function (k) {
        if ((k *= 2) < 1) {
          return 0.5 * k * k * k * k * k;
        }

        return 0.5 * ((k -= 2) * k * k * k * k + 2);
      },
    },

    Sinusoidal: {
      In: function (k) {
        return 1 - Math.cos((k * Math.PI) / 2);
      },

      Out: function (k) {
        return Math.sin((k * Math.PI) / 2);
      },

      InOut: function (k) {
        return 0.5 * (1 - Math.cos(Math.PI * k));
      },
    },

    Exponential: {
      In: function (k) {
        return k === 0 ? 0 : Math.pow(1024, k - 1);
      },

      Out: function (k) {
        return k === 1 ? 1 : 1 - Math.pow(2, -10 * k);
      },

      InOut: function (k) {
        if (k === 0) {
          return 0;
        }

        if (k === 1) {
          return 1;
        }

        if ((k *= 2) < 1) {
          return 0.5 * Math.pow(1024, k - 1);
        }

        return 0.5 * (-Math.pow(2, -10 * (k - 1)) + 2);
      },
    },

    Circular: {
      In: function (k) {
        return 1 - Math.sqrt(1 - k * k);
      },

      Out: function (k) {
        return Math.sqrt(1 - --k * k);
      },

      InOut: function (k) {
        if ((k *= 2) < 1) {
          return -0.5 * (Math.sqrt(1 - k * k) - 1);
        }

        return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1);
      },
    },

    Elastic: {
      In: function (k) {
        if (k === 0) {
          return 0;
        }

        if (k === 1) {
          return 1;
        }

        return -Math.pow(2, 10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI);
      },

      Out: function (k) {
        if (k === 0) {
          return 0;
        }

        if (k === 1) {
          return 1;
        }

        return Math.pow(2, -10 * k) * Math.sin((k - 0.1) * 5 * Math.PI) + 1;
      },

      InOut: function (k) {
        if (k === 0) {
          return 0;
        }

        if (k === 1) {
          return 1;
        }

        k *= 2;

        if (k < 1) {
          return (
            -0.5 * Math.pow(2, 10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI)
          );
        }

        return (
          0.5 * Math.pow(2, -10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI) +
          1
        );
      },
    },

    Back: {
      In: function (k) {
        var s = 1.70158;

        return k * k * ((s + 1) * k - s);
      },

      Out: function (k) {
        var s = 1.70158;

        return --k * k * ((s + 1) * k + s) + 1;
      },

      InOut: function (k) {
        var s = 1.70158 * 1.525;

        if ((k *= 2) < 1) {
          return 0.5 * (k * k * ((s + 1) * k - s));
        }

        return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2);
      },
    },

    Bounce: {
      In: function (k) {
        return 1 - TWEEN.Easing.Bounce.Out(1 - k);
      },

      Out: function (k) {
        if (k < 1 / 2.75) {
          return 7.5625 * k * k;
        } else if (k < 2 / 2.75) {
          return 7.5625 * (k -= 1.5 / 2.75) * k + 0.75;
        } else if (k < 2.5 / 2.75) {
          return 7.5625 * (k -= 2.25 / 2.75) * k + 0.9375;
        } else {
          return 7.5625 * (k -= 2.625 / 2.75) * k + 0.984375;
        }
      },

      InOut: function (k) {
        if (k < 0.5) {
          return TWEEN.Easing.Bounce.In(k * 2) * 0.5;
        }

        return TWEEN.Easing.Bounce.Out(k * 2 - 1) * 0.5 + 0.5;
      },
    },
  };

  TWEEN.Interpolation = {
    Linear: function (v, k) {
      var m = v.length - 1;
      var f = m * k;
      var i = Math.floor(f);
      var fn = TWEEN.Interpolation.Utils.Linear;

      if (k < 0) {
        return fn(v[0], v[1], f);
      }

      if (k > 1) {
        return fn(v[m], v[m - 1], m - f);
      }

      return fn(v[i], v[i + 1 > m ? m : i + 1], f - i);
    },

    Bezier: function (v, k) {
      var b = 0;
      var n = v.length - 1;
      var pw = Math.pow;
      var bn = TWEEN.Interpolation.Utils.Bernstein;

      for (var i = 0; i <= n; i++) {
        b += pw(1 - k, n - i) * pw(k, i) * v[i] * bn(n, i);
      }

      return b;
    },

    CatmullRom: function (v, k) {
      var m = v.length - 1;
      var f = m * k;
      var i = Math.floor(f);
      var fn = TWEEN.Interpolation.Utils.CatmullRom;

      if (v[0] === v[m]) {
        if (k < 0) {
          i = Math.floor((f = m * (1 + k)));
        }

        return fn(
          v[(i - 1 + m) % m],
          v[i],
          v[(i + 1) % m],
          v[(i + 2) % m],
          f - i
        );
      } else {
        if (k < 0) {
          return v[0] - (fn(v[0], v[0], v[1], v[1], -f) - v[0]);
        }

        if (k > 1) {
          return v[m] - (fn(v[m], v[m], v[m - 1], v[m - 1], f - m) - v[m]);
        }

        return fn(
          v[i ? i - 1 : 0],
          v[i],
          v[m < i + 1 ? m : i + 1],
          v[m < i + 2 ? m : i + 2],
          f - i
        );
      }
    },

    Utils: {
      Linear: function (p0, p1, t) {
        return (p1 - p0) * t + p0;
      },

      Bernstein: function (n, i) {
        var fc = TWEEN.Interpolation.Utils.Factorial;

        return fc(n) / fc(i) / fc(n - i);
      },

      Factorial: (function () {
        var a = [1];

        return function (n) {
          var s = 1;

          if (a[n]) {
            return a[n];
          }

          for (var i = n; i > 1; i--) {
            s *= i;
          }

          a[n] = s;
          return s;
        };
      })(),

      CatmullRom: function (p0, p1, p2, p3, t) {
        var v0 = (p2 - p0) * 0.5;
        var v1 = (p3 - p1) * 0.5;
        var t2 = t * t;
        var t3 = t * t2;

        return (
          (2 * p1 - 2 * p2 + v0 + v1) * t3 +
          (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 +
          v0 * t +
          p1
        );
      },
    },
  };
  TWEEN.version = version;

  return TWEEN;
});
